package com.tvunetworks.center.device.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.github.pagehelper.PageInfo;
import com.tvunetworks.center.common.constant.LocalConstant;
import com.tvunetworks.center.common.exception.ServerException;
import com.tvunetworks.center.common.model.ShareFacebookYoutubeDTO;
import com.tvunetworks.center.common.model.dto.share.ShareVideoDTO;
import com.tvunetworks.center.common.model.media.param.MediaOtherAuthInfoParam;
import com.tvunetworks.center.common.model.media.vo.MediaQueryVo;
import com.tvunetworks.center.common.model.share.ShareStatusInfo;
import com.tvunetworks.center.common.util.JsonUtil;
import com.tvunetworks.center.common.util.MyUtil;
import com.tvunetworks.center.common.util.Result;
import com.tvunetworks.center.common.util.StreamUtil;
import com.tvunetworks.center.device.model.CustomizedTShareAccount;
import com.tvunetworks.center.device.model.param.CustomizedShareParam;
import com.tvunetworks.center.device.model.result.ResultConstant;
import com.tvunetworks.center.device.model.vo.CustomizedShareVo;
import com.tvunetworks.center.device.service.CustomizedShareRecordService;
import com.tvunetworks.center.device.service.CustomizedShareService;
import com.tvunetworks.center.device.service.CustomizedTShareAccountService;
import com.tvunetworks.center.device.service.DeviceService;
import com.tvunetworks.center.device.service.feign.MediaFeignService;
import com.tvunetworks.center.device.service.feign.ShareFeignService;
import com.tvunetworks.center.gs.model.Device;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: tvucc-aggregator
 * @description: CustomizedShareServiceImpl
 * @author lebronchen
 * @create: 2020-03-18 10:39
 **/
@Service
@Slf4j
public class CustomizedShareServiceImpl implements CustomizedShareService {


    @Autowired
    private MediaFeignService mediaFeignService;
    @Autowired
    private CustomizedTShareAccountService customizedTShareAccountService;
    @Autowired
    private CustomizedShareRecordService customizedShareRecordService;
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private ShareFeignService shareFeignService;


    @Value("${customized.t.autoShare.address}")
    private String autoShareAddress;
    @Value("${customized.share.default.resolution}")
    private String defaultResolution;
    @Value("${customized.share.default.bitrate}")
    private String defaultBitrate;
    @Value("${customized.t.autoShare.nickname.prefix}")
    private String autoShareNickNamePrefix;
    @Value("${enableAutoShareFixedAddress}")
    private boolean enableAutoShareFixedAddress;


    @Override
    public void startShare(String userId, String rid, String shareId) {
        // 检查参数，并拿到 sid
        String sid = this.checkShareParam(rid, shareId);

        boolean sharing = shareFeignService.checkAccountLiveStatusById(shareId);
        if (sharing) {
            // 推流账号正在推流
            log.error("startShare failed share account is sharing rid:{} sid:{} shareId:{}", rid, sid, shareId);
            ServerException.Exception(ResultConstant.Customized.Share.SHARE_ACCOUNT_IS_SHARING);
        }
        CustomizedTShareAccount tShareAccount = customizedTShareAccountService.getByShareId(shareId);
        if (tShareAccount != null) {
            // 推流地址是固定的推流地址
            // 判断当前固定的推流地址是不是当前 T 的推流地址
            Device source = deviceService.getCurrentDevice(sid);
            if (source == null) {
                log.error("startShare failed source not exist rid:{} sid:{} shareId:{}", rid, sid, shareId);
                ServerException.Exception(ResultConstant.Device.SOURCE_NOT_EXIST);
            }
            if (!LocalConstant.DeviceType.T.equals(source.getType())) {
                log.error("startShare fixedAddress failed source not T rid:{} sid:{} sourceType:{} shareId:{}", rid, sid, source.getType(), shareId);
                ServerException.Exception(ResultConstant.Device.SOURCE_NOT_EXIST);
            }

            // TODO 校验推流地址是否是当前 T 的固定推流地址

            // 推流账号可能不在当前账号下，所以需要取到当时创建这个账号的用户去进行推流
            this.startShareVideo(tShareAccount.getUserId(), shareId, rid, sid);
        } else {
            this.startShareVideo(userId, shareId, rid, sid);
        }
    }

    @Override
    public void stopShare(String userId, String rid, String shareId) {
        // 检查参数，并拿到 sid
        String sid = this.checkShareParam(rid, shareId);

        boolean sharing = shareFeignService.checkAccountLiveStatusById(shareId);
        if (!sharing) {
            // 推流账号不在推流
            log.error("startShare failed share account not sharing rid:{} sid:{} shareId:{}", rid, sid, shareId);
            ServerException.Exception(ResultConstant.Customized.Share.SHARE_ACCOUNT_NOT_SHARING);
        }
        this.stopShareVideo(userId, shareId, rid, sid);

    }

    @Override
    public void tAutoShareFixedAddress(String userId, String rid, String sid) {
        CustomizedTShareAccount shareAccount = this.createAutoShareAccount(userId, sid);
        if (shareAccount == null) {
            log.error("tAutoShareFixedAddress createAutoShareAccount failed return null userId:{} sid:{} rid:{}", userId, sid, rid);
            ServerException.Exception(ResultConstant.Customized.Share.CREATE_SHARE_T_ACCOUNT_FAILED);
        }
        // 推流账号可能不在当前账号下，所以需要取到当时创建这个账号的用户去进行推流
        String shareUserId = shareAccount.getUserId();
        String shareId = shareAccount.getShareId();
        this.startShareVideo(shareUserId, shareId, rid, sid);
    }

    /**
     * 检查 rid 是否在 live，shareId 是否存在，并且返回 sid
     * @param rid
     * @param shareId
     * @return
     */
    private String checkShareParam(String rid, String shareId){
        Device r = deviceService.getCurrentDevice(rid);
        if (r == null) {
            log.error("checkShareParam r not exist rid:{}", rid);
            ServerException.Exception(ResultConstant.Device.R_NOT_EXIST);
        }
        String sid = r.getLivePeerId();
        if (StringUtils.isEmpty(sid)) {
            log.error("checkShareParam r not live rid:{} status:{} livePeerId:{}", rid, r.getStatus(), r.getLivePeerId());
            ServerException.Exception(ResultConstant.Device.R_NOT_LIVING);
        }
        try {
            ShareFacebookYoutubeDTO shareAccount = mediaFeignService.selectShareDTOById(shareId);
            if (shareAccount == null) {
                log.error("checkShareParam shareAccount not exist shareId", shareId);
                ServerException.Exception(ResultConstant.Customized.Share.SHARE_ACCOUNT_NOT_EXIST.setResult(shareId));
            }
        } catch (Exception e) {
            log.error("checkShareParam call media-service selectById exception shareId:{}", shareId);
            log.error(e.toString(), e);
            ServerException.Exception(ResultConstant.Customized.Share.CHECK_SHARE_ACCOUNT_EXCEPTION.setResult(e.toString()));
        }
        return sid;
    }

    private void startShareVideo(String userId, String shareId, String rid, String sid){
        ShareVideoDTO shareVideoDTO = new ShareVideoDTO()
                .setrId(rid)
                .setId(shareId)
                .setLivePeerId(sid);
        try {
            Result<Object> shareResult = shareFeignService.shareVideo(userId, shareVideoDTO);
            if (!shareResult.assertSuccess()) {
                log.error("startShareVideo fixedAddress:{} call share-service shareVideo failed userId:{} rid:{} sid:{} shareVideoDTO:{} result:{}", userId, rid, sid, shareVideoDTO, shareResult);
                ServerException.Exception(ResultConstant.Customized.Share.SHARE_VIDEO_FAILED.setResult(shareResult));
            }
            log.error("startShareVideo fixedAddress:{} call share-service shareVideo success userId:{} rid:{} sid:{} hareVideoDTO:{} result:{}", userId, rid, sid, shareVideoDTO, shareResult);
            customizedShareRecordService.startStopShareAddShareRecord(userId, sid, shareId, true);
        } catch (Exception e) {
            log.error("startShareVideo fixedAddress:{} call share-service shareVideo exception userId:{} rid:{} sid:{} shareVideoDTO:{}", userId, rid, sid, shareVideoDTO);
            log.error(e.toString(), e);
            ServerException.Exception(ResultConstant.Customized.Share.SHARE_VIDEO_EXCEPTION.setResult(e.toString()));
        }
    }

    private void stopShareVideo(String userId, String shareId, String rid, String sid){
        ShareVideoDTO shareVideoDTO = new ShareVideoDTO()
                .setrId(rid)
                .setId(shareId);
        try {
            Result<Object> shareResult = shareFeignService.stopShareVideo(shareVideoDTO);
            if (!shareResult.assertSuccess()) {
                log.error("startShareVideo fixedAddress:{} call share-service stopShareVideo failed rid:{} sid:{} shareVideoDTO:{} result:{}", rid, sid, shareVideoDTO, shareResult);
                ServerException.Exception(ResultConstant.Customized.Share.STOP_SHARE_VIDEO_FAILED.setResult(shareResult));
            }
            log.error("startShareVideo fixedAddress:{} call share-service stopShareVideo success rid:{} sid:{} shareVideoDTO:{} result:{}", rid, sid, shareVideoDTO, shareResult);
            customizedShareRecordService.startStopShareAddShareRecord(userId, sid, shareId, false);
        } catch (IOException e) {
            log.error("startShareVideo fixedAddress:{} call share-service stopShareVideo exception rid:{} sid:{} shareVideoDTO:{}", rid, sid, shareVideoDTO);
            log.error(e.toString(), e);
            ServerException.Exception(ResultConstant.Customized.Share.STOP_SHARE_VIDEO_EXCEPTION.setResult(e.toString()));
        }
    }

    @Override
    @Transactional
    public CustomizedTShareAccount createAutoShareAccount(String userId, String peerId) {
        // 校验设备是否是 T
        Device device = deviceService.getCurrentDevice(peerId);
        if (device == null) {
            log.error("createAutoShareAccount failed device not exist peerId:{}", peerId);
            ServerException.Exception(ResultConstant.Device.DEVICE_NOT_EXIST);
        }
        if (!LocalConstant.DeviceType.T.equals(device.getType())) {
            log.error("createAutoShareAccount failed device not T peerId:{} type:{}", peerId, device.getType());
            ServerException.Exception(ResultConstant.Device.DEVICE_NOT_T);
        }

        String deviceName = device.getName();

        // 校验该自动推流账号是否已经创建过
        CustomizedTShareAccount exist = customizedTShareAccountService.getTShareAccountExist(peerId, deviceName);
        if (exist != null) {
            // 检查该推流账号是否已经被删除
            String shareId = exist.getShareId();
            MediaQueryVo mediaQueryVo = mediaFeignService.selectAccountById(shareId);
            if (mediaQueryVo != null) {
                return exist;
            } else {
                // 推流账号已经被删除，需要删除 CustomizedTShareAccount
                customizedTShareAccountService.deleteById(exist.getId());
            }
        }

        // 调用 media 服务创建推流账号
        String nickName = autoShareNickNamePrefix + MyUtil.getUUID().substring(10);
        MediaOtherAuthInfoParam param = new MediaOtherAuthInfoParam()
                .setRtmpUrl(autoShareAddress + "/" + deviceName)
                .setAuthType("1")
                .setResolution(defaultResolution)
                .setBitrate(defaultBitrate)
                .setNickName(nickName)
                .setUserId(userId);
        try {
            Result<String> addShareAccountResult = mediaFeignService.saveOrUpdateAuthInfo(param);
            if (!addShareAccountResult.assertSuccess()) {
                log.error("createAutoShareAccount call media-service saveOrUpdateAuthInfo failed param:{} result:{}", param, addShareAccountResult);
                ServerException.Exception(ResultConstant.Customized.Share.CREATE_SHARE_ACCOUNT_FAILED.setResult(addShareAccountResult));
            }

            // 把创建的推流账号记录下来
            String shareAccountId = addShareAccountResult.getResult();
            CustomizedTShareAccount customizedTShareAccount = new CustomizedTShareAccount()
                    .setPeerId(peerId)
                    .setDeviceName(deviceName)
                    .setShareId(shareAccountId)
                    .setShareNickName(nickName)
                    .setUserId(userId);
            log.error("createAutoShareAccount call media-service saveOrUpdateAuthInfo success param:{} result:{}", param, addShareAccountResult);
            CustomizedTShareAccount tShareAccount = customizedTShareAccountService.addTShareAccount(customizedTShareAccount);
            return tShareAccount;
        } catch (Exception e) {
            log.error("createAutoShareAccount call media-service saveOrUpdateAuthInfo exception param:{}", param);
            log.error(e.toString(), e);
            ServerException.Exception(ResultConstant.Customized.Share.CREATE_SHARE_ACCOUNT_EXCEPTION.setResult(e.toString()));
        }
        return null;
    }

    @Override
    public void stopAllShare(String userId, String rid, String sid) {
        List<ShareStatusInfo> shareAccountInfo = null;
        try {
            shareAccountInfo = shareFeignService.getSingleRShareAccountInfo(rid);
        } catch (Exception e) {
            log.error("stopAllShare call share-service getRShareAccountInfo exception rid:{}", rid);
            log.error(e.toString(), e);
            ServerException.Exception(ResultConstant.Customized.Share.QUERY_R_SHARE_ACCOUNT_EXCEPTION.setResult(e.toString()));
        }
        if (CollectionUtils.isEmpty(shareAccountInfo)) {
            log.error("stopAllShare success r not share any account rid:{}", rid);
            return;
        }

        // 调用 share 提供的删除 R 所有推流的接口
        try {
            Result<Object> stopRAllShareResult = shareFeignService.stopRAllShare(rid);
            if (!stopRAllShareResult.assertSuccess()) {
                log.error("stopAllShare call share-service stopRAllShare failed rid:{} result:{}", rid, stopRAllShareResult);
            }
        } catch (Exception e) {
            log.error("stopAllShare call share-service stopRAllShare exception rid:{}", rid);
            log.error(e.toString(), e);
        }

        List<String> nickNames = shareAccountInfo.stream().filter(StreamUtil.distinctByKey(ShareStatusInfo::getNickName)).map(ShareStatusInfo::getNickName).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(nickNames)) {
            log.error("stopAllShare success shareNickNames is empty rid:{}", rid);
            return;
        }

        // 根据 share nickName 获取 shareId
        List<String> shareIds = mediaFeignService.selectShareIdsByNickNames(nickNames);
        // 添加推流记录到 ShareRecord
        for (String shareId : shareIds) {
            customizedShareRecordService.stopLiveAddShareRecord(userId, sid, shareId);
        }
    }

    @Override
    public List<CustomizedShareVo> listShareAccount(String userId, CustomizedShareParam param) {
        List<CustomizedShareVo> resultList = new ArrayList<>();
        if (enableAutoShareFixedAddress) {
            // 查询 fixed 推流地址
            CustomizedTShareAccount fixedShareAccount = this.createAutoShareAccount(userId, param.getTid());
            if (fixedShareAccount != null) {
                MediaQueryVo mediaQueryVo = mediaFeignService.selectAccountById(fixedShareAccount.getShareId());
                if (mediaQueryVo != null) {
                    CustomizedShareVo vo = JsonUtil.convertValue(mediaQueryVo, CustomizedShareVo.class);
                    vo.setFixed(true);
                    resultList.add(vo);
                } else {
                    log.error("listShareAccount selectAccountById return null userId:{} tid:{} shareId:{}", userId, param.getTid(), fixedShareAccount.getShareId());
                }
            } else {
                log.error("listShareAccount selectAccountById failed userId:{} tid:{}", userId, param.getTid());
            }
        }


        // 查询非 fixed 推流地址
        PageInfo<MediaQueryVo> pageInfo = mediaFeignService.listAccountInfo(userId, null, null, 1, 99999);
        if (pageInfo != null) {
            pageInfo.getList();
            List<MediaQueryVo> list = pageInfo.getList();
            if (!CollectionUtils.isEmpty(list)) {
                if (enableAutoShareFixedAddress) {
                    // 剔除掉 Fixed 推流地址
                    List<String> allFixedShareIds = customizedTShareAccountService.listAllShareIds();
                    if (!CollectionUtils.isEmpty(allFixedShareIds)) {
                        Iterator<MediaQueryVo> iterator = list.iterator();
                        while (iterator.hasNext()) {
                            MediaQueryVo next = iterator.next();
                            if (allFixedShareIds.contains(next.getId())) {
                                iterator.remove();
                            }
                        }
                    }
                }
            }

            if (!CollectionUtils.isEmpty(list)) {
                // 剔除 Fixed 推流地址之后，保留其他所有推流地址
                List<CustomizedShareVo> vos = JsonUtil.convertValue(list, new TypeReference<List<CustomizedShareVo>>() {});
                resultList.addAll(vos);
            }
        }

        String rid = param.getRid();
        if (StringUtils.isNotBlank(rid)) {
            // 获取当前 R 正在推流的 shareId
            List<String> currentRSharingNickNames = new ArrayList<>();

            List<ShareStatusInfo> currentRShareInfo = null;
            try {
                currentRShareInfo = shareFeignService.getSingleRShareAccountInfo(rid);
            } catch (Exception e) {
                log.error("listShareAccount call share-service getRShareAccountInfo exception rid:{}", rid);
                log.error(e.toString(), e);
                ServerException.Exception(ResultConstant.Customized.Share.QUERY_R_SHARE_ACCOUNT_EXCEPTION.setResult(e.toString()));
            }
            if (!CollectionUtils.isEmpty(currentRShareInfo)) {
                currentRSharingNickNames = currentRShareInfo.stream().map(ShareStatusInfo::getNickName).collect(Collectors.toList());
            }

            // 获取所有正在推流的 nickName
            List<String> sharingNickNames = new ArrayList<>();
            Set<String> nickNames = resultList.stream().map(CustomizedShareVo::getNickName).collect(Collectors.toSet());
            Map<String, Boolean> sharingMap = shareFeignService.checkAccountLiveStatusInfoByUser(nickNames, userId);
            if (!CollectionUtils.isEmpty(sharingMap)) {
                for (Map.Entry<String, Boolean> entry : sharingMap.entrySet()) {
                    if (entry.getValue()) {
                        sharingNickNames.add(entry.getKey());
                    }
                }
            }

            for (CustomizedShareVo vo : resultList) {
                String nickName = vo.getNickName();
                if (currentRSharingNickNames.contains(nickName)) {
                    vo.setShareFlag(CustomizedShareVo.SHARE_FLAG_SHARE_WITH_CURRENT_R);
                    continue;
                }
                if (sharingNickNames.contains(nickName)) {
                    vo.setShareFlag(CustomizedShareVo.SHARE_FLAG_SHARE_WITH_OTHER_R);
                    continue;
                }
            }
        }


        // 排序  fixed 最先，然后 SHARE_FLAG_SHARE_WITH_CURRENT_R 然后 SHARE_FLAG_SHARE_WITH_OTHER_R
        resultList.stream().sorted(Comparator.comparing(CustomizedShareVo::getShareFlag)).sorted(Comparator.comparing(CustomizedShareVo::isFixed));

        return resultList;
    }
}
